package com.xvyy.tingshu.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.xvyy.tingshu.constant.CacheAbleConstant;
import com.xvyy.tingshu.service.CacheOpsService;
import com.xvyy.tingshu.utils.JSONDataFormatting;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * ClassName: CacheOpsServiceImpl
 * Package: com.xvyy.tingshu.service.impl
 *
 * @Description:
 * @Author: xvyy
 * @Create: 2025/2/9 - 14:07
 * @Version: v1.0
 */
public class CacheOpsServiceImpl implements CacheOpsService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public <T> T getDataFromCache(String cacheKey, Class<T> clazz) {
        // 1. 查询缓存
        String jsonStringFromCache = stringRedisTemplate.opsForValue().get(cacheKey);
        if (StringUtils.isEmpty(jsonStringFromCache)) {
            // 2. 缓存中没有查到数据，直接返回null
            return null;
        }
        // 3. 反序列化,返回结果
        return JSONDataFormatting.jsonToObject(jsonStringFromCache, clazz);
    }

    @Override
    public void saveDataToCache(String cacheKey, Object o) {
        // 1. 序列化
        String serializableStr = JSONDataFormatting.ObjectToStr(o);
        long ttl;
        // 2. 设置过期时间，判断是否是固定值
//        ttl = basicMode(serializableStr);
        ttl = chainOfResponsibilityPattern(serializableStr);
        // 3. 保存数据到缓存中
        // 给过期时间，ttl：(解决最终一致性，以及冷数据不占内存空间问题)
        stringRedisTemplate.opsForValue().set(cacheKey, serializableStr, ttl, TimeUnit.MINUTES);
    }

    /**
     * 基础模式，判断是否是固定值
     * @param serializableStr
     * @return
     */
    private long basicMode(String serializableStr) {
        long ttl;
        if (regexRule("^null$", serializableStr) || regexRule("^\\{\\}$", serializableStr) || regexRule("^\\[\\]$", serializableStr)) {
            // 2.1 设置过期时间短一点，防止缓存穿透固定值攻击
            ttl = CacheAbleConstant.SYNC_HAS_NO_DATA_TTL;
        } else {
            // 2.2 设置过期时间长一点
            ttl = CacheAbleConstant.SYNC_HAS_DATA_TTL;
        }
        return ttl;
    }

    /**
     * 责任链模式，判断是否为固定值
     *
     * @param serializableStr
     * @return
     */
    private long chainOfResponsibilityPattern(String serializableStr) {
        long ttl;
        ttl = CacheAbleConstant.SYNC_HAS_DATA_TTL;
        for (String regex : JSONDataFormatting.getAppAllRegex()) {
            // "null" "{}" "[]" "{完整数据的json}"
            if (regexRule(regex, serializableStr)) {
                ttl = CacheAbleConstant.SYNC_HAS_NO_DATA_TTL;
            }
        }
        return ttl;
    }

    @Override
    public <T> T getDataFromCache(String cacheKey, TypeReference<T> typeReference) {
        // 1. 查询缓存
        String jsonStringFromCache = stringRedisTemplate.opsForValue().get(cacheKey);
        if (StringUtils.isEmpty(jsonStringFromCache)) {
            // 2.  缓存中没有查到数据，直接返回null
            return null;
        }
        // 3. 反序列化，返回结果
        return JSONDataFormatting.jsonToObject(jsonStringFromCache, typeReference);
    }

    /**
     * 正则匹配规则
     *
     * @param rule
     * @param compareContent
     * @return boolean
     */
    public boolean regexRule(String rule, String compareContent) {
        return Pattern.matches(rule, compareContent);
    }

}
